package com.ctsi.commons.util;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
 
/**
 * 优化的byte stream ,去掉同步,增加默认大小,避免数据复制,返回的数据,一般不要修改
 * @author yueming
 *
 */
public final class ByteOutputStream extends OutputStream {

	/**
	 * The buffer where data is stored.
	 */
	protected  byte buf[];

	/**
	 * The number of valid bytes in the buffer.
	 */
	protected int count;

	/**
	 * Creates a new byte array output stream. The buffer capacity is initially 8k bytes, though its size increases if necessary.
	 */
	public ByteOutputStream() {
		this(1024*8);
	}

	/**
	 * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
	 * 
	 * @param size
	 *            the initial size.
	 * @exception IllegalArgumentException
	 *                if size is negative.
	 */
	public ByteOutputStream(int size) {
		if (size < 0) {
			throw new IllegalArgumentException("Negative initial size: " + size);
		}
		buf = new byte[size];
	}

	/**
	 * Writes the specified byte to this byte array output stream.
	 * 
	 * @param b
	 *            the byte to be written.
	 */
	public void write(int b) {
		int newcount = count + 1;
		if (newcount > buf.length) {
			buf = copyOf(buf, Math.max(buf.length << 1, newcount));
		}
		buf[count] = (byte) b;
		count = newcount;
	}
    private static final byte[] copyOf(byte[] original, int newLength) {
        byte[] copy = new byte[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
	/**
	 * Writes <code>len</code> bytes from the specified byte array starting at offset <code>off</code> to this byte array output stream.
	 * 
	 * @param b
	 *            the data.
	 * @param off
	 *            the start offset in the data.
	 * @param len
	 *            the number of bytes to write.
	 */
	public void write(byte b[], int off, int len) {
		if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
			throw new IndexOutOfBoundsException();
		} else if (len == 0) {
			return;
		}
		int newcount = count + len;
		if (newcount > buf.length) {
			buf = copyOf(buf, Math.max(buf.length << 1, newcount));
		}
		System.arraycopy(b, off, buf, count, len);
		count = newcount;
	}

	/**
	 * Writes the complete contents of this byte array output stream to the specified output stream argument, as if by calling the output stream's write method
	 * using <code>out.write(buf, 0, count)</code>.
	 * 
	 * @param out
	 *            the output stream to which to write the data.
	 * @exception IOException
	 *                if an I/O error occurs.
	 */
	public void writeTo(OutputStream out) throws IOException {
		out.write(buf, 0, count);
	}

	/**
	 * Resets the <code>count</code> field of this byte array output stream to zero, so that all currently accumulated output in the output stream is discarded.
	 * The output stream can be used again, reusing the already allocated buffer space.
	 * 
	 * @see java.io.ByteArrayInputStream#count
	 */
	public void reset() {
		count = 0;
	}

	/**
	 * 返回数据,如果数据填满buffer,直接返回,负责复制返回
	 * 
	 * @return 当前内容
	 * @see java.io.ByteArrayOutputStream#size()
	 */
	public byte[] toByteArray() {
		if (buf.length==count) return buf;
		return copyOf(buf, count);
	}
	/**
	 * 直接返回,避免数据复制
	 * @return
	 */
	public byte[] byteArray(){
		return buf;
	}

	/**
	 * Returns the current size of the buffer.
	 * 
	 * @return the value of the <code>count</code> field, which is the number of valid bytes in this output stream.
	 * @see java.io.ByteArrayOutputStream#count
	 */
	public int size() {
		return count;
	}

	/**
	 * Converts the buffer's contents into a string decoding bytes using the platform's default character set. The length of the new <tt>String</tt> is a
	 * function of the character set, and hence may not be equal to the size of the buffer.
	 * 
	 * <p>
	 * This method always replaces malformed-input and unmappable-character sequences with the default replacement string for the platform's default character
	 * set. The {@linkplain java.nio.charset.CharsetDecoder} class should be used when more control over the decoding process is required.
	 * 
	 * @return String decoded from the buffer's contents.
	 * @since JDK1.1
	 */
	public String toString() {
		return new String(buf, 0, count);
	}

	/**
	 * Converts the buffer's contents into a string by decoding the bytes using the specified {@link java.nio.charset.Charset charsetName}. The length of the
	 * new <tt>String</tt> is a function of the charset, and hence may not be equal to the length of the byte array.
	 * 
	 * <p>
	 * This method always replaces malformed-input and unmappable-character sequences with this charset's default replacement string. The
	 * {@link java.nio.charset.CharsetDecoder} class should be used when more control over the decoding process is required.
	 * 
	 * @param charsetName
	 *            the name of a supported {@linkplain java.nio.charset.Charset </code>charset<code>}
	 * @return String decoded from the buffer's contents.
	 * @exception UnsupportedEncodingException
	 *                If the named charset is not supported
	 * @since JDK1.1
	 */
	public String toString(String charsetName) throws UnsupportedEncodingException {
		return new String(buf, 0, count, charsetName);
	}
	public String toString(Charset charsetName)   {
		return new String(buf, 0, count, charsetName);
	}
	/**
	 * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in this class can be called after the stream has been closed without generating an
	 * <tt>IOException</tt>.
	 * <p>
	 * 
	 */
	public void close() {
	}

	public int write(ByteBuffer src) {
        int len = src.remaining();
        write(src.array(), src.position(), len);
        src.position(src.position() + len);
        return len;
    }
}
